package com.baitiaojun.moments.aspect;

import com.baitiaojun.api.exception.ConditionException;
import com.baitiaojun.api.util.ConstantUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * @类名 LockAspect
 * @描述 TODO
 * @作者 白条君
 * @创建日期 2023/1/31 00:30
 * @版本 1.0
 */
@Component
@Aspect
public class LockAspect {

    @Pointcut("@annotation(com.baitiaojun.api.annotation.Lock)")
    public void pointCut() {}

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Long videoId = null;
        Integer count = null;
        Object[] args = point.getArgs();
        for (Object arg : args) {
            if (arg instanceof Long) {
                videoId = (Long) arg;
            } else if (arg instanceof Integer) {
                count = (Integer) arg;
            }
        }
        if (ObjectUtils.isEmpty(videoId) || ObjectUtils.isEmpty(count)) {
            throw new ConditionException("参数异常");
        }
        Object res = 0;
        String methodName = point.getSignature().getName();
        if ("updateVideoViewCount".equals(methodName)) {
            Lock lock = ConstantUtil.LOCK_VIEW_COUNT;
            res = this.execute(lock, videoId, count, point);
        } else if("updateVideoCommentCount".equals(methodName)) {
            Lock lock = ConstantUtil.LOCK_COMMENT_COUNT;
            res = this.execute(lock, videoId, count, point);
        } else if ("updateVideoLikeCount".equals(methodName)) {
            Lock lock = ConstantUtil.LOCK_LIKE_COUNT;
            res = this.execute(lock, videoId, count, point);
        }
        return res;
    }

    private Object execute(Lock lock, Long videoId, Integer count, ProceedingJoinPoint point) throws Throwable {
        Object res;
        try {
            while (true) {
                if (lock.tryLock(5, TimeUnit.SECONDS)) {
                    Object[] params = {videoId, count};
                    res = point.proceed(params);
                    break;
                }
            }
        } finally {
            lock.unlock();
        }
        return res;
    }
}
